/*
 * Copyright 2013 serso aka se.solovyev
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * Contact details
 *
 * Email: se.solovyev@gmail.com
 * Site:  http://se.solovyev.org
 */

package org.solovyev.android.calculator.matrix;

import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.TableLayout;
import android.widget.TableRow;
import android.widget.TextView;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

/**
 * User: Solovyev_S
 * Date: 12.10.12
 * Time: 15:41
 */
public class MatrixView extends TableLayout {

	/*
	**********************************************************************
	*
	*                           CONSTANTS
	*
	**********************************************************************
	*/

	private static final CharSequence DEFAULT_CELL_TEXT = "0";
	private static final int NUMBER_INDEX = -1;


	/*
	**********************************************************************
	*
	*                           FIELDS
	*
	**********************************************************************
	*/

	private int rows = 0;
	private int cols = 0;

	@Nullable
	private CharSequence defaultCellText = DEFAULT_CELL_TEXT;

	private boolean initialized = false;

	/*
	**********************************************************************
	*
	*                           CONSTRUCTORS
	*
	**********************************************************************
	*/

	public MatrixView(Context context) {
		super(context);
	}

	public MatrixView(Context context, AttributeSet attrs) {
		super(context, attrs);
	}

	/*
	**********************************************************************
	*
	*                           METHODS
	*
	**********************************************************************
	*/

	public int getRows() {
		return rows;
	}

	public int getCols() {
		return cols;
	}

	public void setMatrixCols(int newCols) {
		setMatrixDimensions(rows, newCols);
	}

	public void setMatrixRows(int newRows) {
		setMatrixDimensions(newRows, cols);
	}

	public void setMatrixDimensions(int newRows, int newCols) {
		if (newRows <= 1) {
			throw new IllegalArgumentException("Number of rows must be more than 1: " + newRows);
		}

		if (newCols <= 1) {
			throw new IllegalArgumentException("Number of columns must be more than 1: " + newCols);
		}

		final boolean rowsChanged = this.rows != newRows;
		final boolean colsChanged = this.cols != newCols;

		if (rowsChanged || colsChanged) {
			if (!initialized) {
				addRow(NUMBER_INDEX, 0);
				initialized = true;
			}

			if (this.cols > newCols) {
				removeCols(newCols);
			} else if (this.cols < newCols) {
				addCols(newCols);
			}

			this.cols = newCols;

			if (this.rows > newRows) {
				removeRows(newRows);
			} else if (this.rows < newRows) {
				addRows(newRows);
			}

			this.rows = newRows;
		}
	}

	public void setMatrix(@Nonnull Object[][] matrix) {
		final int rows = matrix.length;
		final int cols = matrix[0].length;

		setMatrixDimensions(rows, cols);
		for (int row = 0; row < rows; row++) {
			for (int col = 0; col < cols; col++) {
				setCell(row, col, matrix[row][col]);
			}
		}
	}

	@Nonnull
	public String[][] toMatrix() {
		final String[][] result = new String[rows][cols];

		for (int row = 0; row < rows; row++) {
			for (int col = 0; col < cols; col++) {
				final TextView cellTextView = (TextView) getCell(this, row, col);
				if (cellTextView != null) {
					result[row][col] = cellTextView.getText().toString();
				}
			}
		}

		return result;
	}

	private void setCell(int row, int col, @Nullable Object o) {
		final TextView cellTextView = (TextView) getCell(this, row, col);
		if (cellTextView != null) {
			if (o == null) {
				cellTextView.setText(null);
			} else {
				cellTextView.setText(String.valueOf(o));
			}
		}
	}

	/*
	**********************************************************************
	*
	*                           PRIVATE METHODS
	*
	**********************************************************************
	*/

	private void addRows(int newRows) {
		for (int row = this.rows; row < newRows; row++) {
			addRow(row, cols);
		}
	}

	private void removeRows(int newRows) {
		for (int row = this.rows - 1; row >= newRows; row--) {
			removeRow(row);
		}
	}

	private void addCols(int newCols) {
		for (int row = NUMBER_INDEX; row < rows; row++) {
			final ViewGroup rowView = getRow(row);
			if (rowView != null) {
				for (int col = this.cols; col < newCols; col++) {
					rowView.addView(createCellView(row, col));
				}
			}
		}
	}

	private void removeCols(int newCols) {
		for (int row = NUMBER_INDEX; row < rows; row++) {
			final ViewGroup rowView = getRow(row);
			if (rowView != null) {
				for (int col = this.cols - 1; col >= newCols; col--) {
					final View cellView = getCell(rowView, row, col);
					if (cellView != null) {
						rowView.removeView(cellView);
					}
				}
			}
		}
	}

	private void addRow(int row, int newCols) {
		this.addView(createRowView(row, newCols));
	}

	private void removeRow(int row) {
		final View rowView = getRow(row);
		if (rowView != null) {
			this.removeView(rowView);
		}
	}

	@Nullable
	private TableRow getRow(int row) {
		return (TableRow) this.findViewWithTag(getRowTag(row));
	}

	@Nullable
	private View getCell(@Nonnull View view, int row, int col) {
		return view.findViewWithTag(getCellTag(row, col));
	}

	@Nonnull
	private String getRowTag(int row) {
		if (row != NUMBER_INDEX) {
			return "row_" + row;
		} else {
			return "row_index";
		}
	}

	@Nonnull
	private View createRowView(int row, int cols) {
		final ViewGroup rowView = new TableRow(this.getContext());

		rowView.setTag(getRowTag(row));

		if (row != NUMBER_INDEX) {
			rowView.addView(createCellView(row, NUMBER_INDEX));
		} else {
			// empty cell
			rowView.addView(new View(this.getContext()));
		}

		for (int col = 0; col < cols; col++) {
			rowView.addView(createCellView(row, col));
		}
		return rowView;
	}

	@Nonnull
	private View createCellView(int row, int col) {
		final TextView result;

		if (row != NUMBER_INDEX && col != NUMBER_INDEX) {
			result = new EditText(this.getContext());
			result.setText(defaultCellText);
		} else {
			result = new TextView(this.getContext());
			if (row == NUMBER_INDEX) {
				result.setText(String.valueOf(col + 1));
			} else {
				result.setText(String.valueOf(row + 1));
			}
		}

		result.setTag(getCellTag(row, col));

		return result;

	}

	@Nonnull
	private String getCellTag(int row, int col) {
		if (row != NUMBER_INDEX) {
			return "cell_" + row + "_" + col;
		} else {
			return "cell_index_" + col;
		}
	}

}
